using System;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using ChessKit.Common;

namespace ChessKit.Ui
{
  public class BoardBorderControl : Control
  {
    #region ' BoardPanel Field '

    private MasterBoardPanel _boardPanel;

    public MasterBoardPanel BoardPanel
    {
      get { return _boardPanel; }
      set
      {
        if (_boardPanel == value) return;
        if (_boardPanel != null)
        {
        }
        _boardPanel = value;
        if (_boardPanel != null)
        {
          _boardPanel.Transform = IsRotated 
            ? BoardTransform.Identity 
            : BoardTransform.Rotated;
          SetupDisposition();
        }
      }
    }

    #endregion

    #region ' ShowLines Property '

    public static readonly DependencyProperty ShowLinesProperty =
      DependencyProperty.Register("ShowLines", typeof(bool),
      typeof(BoardBorderControl), new PropertyMetadata(true));

    public bool ShowLines
    {
      get { return (bool)GetValue(ShowLinesProperty); }
      set { SetValue(ShowLinesProperty, value); }
    }

    #endregion

    #region ' ShowCoordinates Property '

    public static readonly DependencyProperty ShowCoordinatesProperty =
      DependencyProperty.Register("ShowCoordinates", typeof(bool),
      typeof(BoardBorderControl), new PropertyMetadata(true));

    public bool ShowCoordinates
    {
      get { return (bool)GetValue(ShowCoordinatesProperty); }
      set { SetValue(ShowCoordinatesProperty, value); }
    }

    #endregion

    #region ' IsRotated Property '

    public static readonly DependencyProperty IsRotatedProperty =
      DependencyProperty.Register("IsRotated", typeof(bool), typeof(BoardBorderControl),
      new PropertyMetadata(default(bool), OnIsRotatedChanged));

    private static void OnIsRotatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((BoardBorderControl)d).OnIsRotatedChanged((bool)e.NewValue);
    }
    private void OnIsRotatedChanged(bool value)
    {
      if (_boardPanel != null)
      {
        _boardPanel.Transform = value ? BoardTransform.Identity : BoardTransform.Rotated;
      }
    }

    public bool IsRotated
    {
      get { return (bool)GetValue(IsRotatedProperty); }
      set { SetValue(IsRotatedProperty, value); }
    }

    #endregion

    #region ' Colorizers Property '

    public static readonly DependencyProperty ColorizersProperty =
      DependencyProperty.Register("Colorizers",
      typeof(Colorizers), typeof(BoardBorderControl),
      new PropertyMetadata(null, OnColorizersChanged));

    private static void OnColorizersChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
      ((BoardBorderControl)dependencyObject).
        OnColorizersChanged((Colorizers)e.OldValue, (Colorizers)e.NewValue);
    }
    private void OnColorizersChanged(Colorizers oldValue, Colorizers newValue)
    {
      if (oldValue != null)
      {
        oldValue.Clear();
        oldValue.CollectionChanged -= OnColorizersChanged;
      }

      if (newValue != null)
      {
        newValue.CollectionChanged += OnColorizersChanged;
      }
    }

    public Colorizers Colorizers
    {
      get { return (Colorizers)GetValue(ColorizersProperty); }
      set { SetValue(ColorizersProperty, value); }
    }

    private void OnColorizersChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      switch (e.Action)
      {
        case NotifyCollectionChangedAction.Add:
          foreach (IColorizer colorizer in e.NewItems)
            if (colorizer != null)
              AddColorizer(colorizer);
            else throw new InvalidOperationException(
              "Cannot add <null> colorizer into Colorizers collection of the BoardControl");
          break;
        case NotifyCollectionChangedAction.Remove:
          foreach (IColorizer colorizer in e.OldItems)
            RemoveColorizer(colorizer);
          break;
        default: throw new NotSupportedException();
      }
    }

    private void RemoveColorizer(IColorizer colorizer)
    {
      colorizer.Update -= OnColorizerUpdated;
      UpdateColorizer(colorizer, false);
    }

    private void AddColorizer(IColorizer colorizer)
    {
      colorizer.Update += OnColorizerUpdated;
      UpdateColorizer(colorizer, true);
    }

    private void OnColorizerUpdated(object s, EventArgs e)
    {
      UpdateColorizer((IColorizer) s, true);
    }

    private void UpdateColorizer(IColorizer colorizer, bool set)
    {
      if (_boardPanel == null) return;
      foreach (var cell in _boardPanel.Children.OfType<CellControl>())
        cell.SetCurrentValue(colorizer.Property, set 
          ? colorizer.Test(Ui.BoardPanel.GetLocation(cell))
          : DependencyProperty.UnsetValue);
    }

    #endregion

    #region ' Initialization '

    static BoardBorderControl()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(BoardBorderControl),
        new FrameworkPropertyMetadata(typeof(BoardBorderControl)));
    }

    public BoardBorderControl()
    {
      Colorizers = new Colorizers();
    }

    public override void OnApplyTemplate()
    {
      BoardPanel = this.FindVisualChildren<MasterBoardPanel>().FirstOrDefault();
      base.OnApplyTemplate();
    }

    #endregion

    private void SetupDisposition()
    {
      if (_boardPanel == null) return;
      _boardPanel.Children.Clear();

      for (int i = 0; i < 9; i++)
      {
        if (ShowLines)
        {
          Put<GridLine>(BoardLocation.ShortRow(i));
          Put<GridLine>(BoardLocation.ShortColumn(i));
        }
        if (ShowCoordinates && i > 0)
        {
          Put<VerticalEdge>(BoardLocation.LeftEdge(i)).Value = i;
          Put<VerticalEdge>(BoardLocation.RightEdge(i)).Value = i;
          Put<HorizontalEdge>(BoardLocation.TopEdge(i)).Value = i;
          Put<HorizontalEdge>(BoardLocation.BottomEdge(i)).Value = i;
        }
      }

      foreach (var cell in BoardLocation.Cells)
        Put<CellControl>(cell);
    }

    private T Put<T>(BoardLocation location)
      where T : Control, new()
    {
      var element = new T();
      Ui.BoardPanel.SetLocation(element, location);
      _boardPanel.Children.Add(element);
      return element;
    }
  }
}